﻿namespace VIT.BusinessLogicLayer
{
    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Linq;

    using VIT.DataAccessLayer;
    using VIT.DataTransferObjects.Presentation;
    using VIT.Entity;
    using VIT.Library;

    public class CategoryBLL : BLLBase
    {
        private readonly ICategoryLanguageDAL _categoryLanguageDAL;
        private readonly ICategoryDAL _categoryDAL;

        private readonly string _domainStore;
        private readonly string _imagePath;
        
        #region Constructor

        public CategoryBLL(string connectionString = "")
            : base(connectionString)
        {
            this._categoryLanguageDAL = new CategoryLanguageDAL(this.DatabaseFactory);
            this._categoryDAL = new CategoryDAL(this.DatabaseFactory);

            this._domainStore = SettingsManager.AppSettings.DomainStore;
            this._imagePath = SettingsManager.AppSettings.CategoryImagePath;
        }

        #endregion

        #region Function
        public string GetTypeByCategoryId(int companyId, int categoryId)
        {
            var query = this._categoryDAL.GetAll()
                 .Where(e => e.Item.CompanyId == companyId && e.Item.IsPublished && !e.Item.HasDeleted && e.Item.Id == categoryId)
                 .Select(e => e.TypeId);

            var data = query.FirstOrDefault();

            return data;
        }

        public DataSimpleDto GetDataSingle(int companyId, string languageId, int id)
        {
            var query = this._categoryLanguageDAL.GetAll()
                .Where(e => e.Category.Item.CompanyId == companyId && e.LanguageId == languageId && e.Category.Item.IsPublished && !e.Category.Item.HasDeleted && e.CategoryId == id);

            var selected = query.Select(a => new DataSimpleDto
            {
                Id = a.CategoryId,
                CategoryId = a.Category.ParentId ?? 0,
                Title = a.Title,
                Description = a.Description,
                ImagePath = a.Category.Image
            });

            var data = selected.FirstOrDefault();

            if (data != null && !string.IsNullOrEmpty(data.ImagePath))
            {
                var imagePathThumb = string.Format(this._imagePath, companyId);
                data.ImagePath = this.VirtualPath(imagePathThumb, data.ImagePath);
            }

            return data;
        }

        public List<DataSimpleDto> GetDataSimple(int companyId, string languageId, int categoryId, bool inChildCat, out int totalPage, int Skip = 0, int Take = 0)
        {
            var query = this._categoryLanguageDAL.GetAll()
                .Where(e => e.Category.Item.CompanyId == companyId && e.LanguageId == languageId && e.Category.Item.IsPublished && !e.Category.Item.HasDeleted);
                
            if (categoryId > 0)
            {
                IList<int> listCategory = new List<int>();
                if (inChildCat)
                {
                    listCategory = this._categoryDAL.GetAllChildId(categoryId, companyId);
                }

                listCategory.Add(categoryId);
                query = query.Where(c => listCategory.Contains(c.Category.ParentId ?? 0));
            }

            var selected = query.OrderBy(e => e.Category.Item.Orders)
                .ThenByDescending(e => e.Category.Item.CreateDate)
                .Select(a => new DataSimpleDto
                    {
                        Id = a.CategoryId,
                        CategoryId = a.Category.ParentId ?? 0,
                        Title = a.Title,
                        Description = a.Description,
                        ImagePath = a.Category.Image
                    });

            totalPage = selected.Count();
            if (Skip > 0) selected = selected.Skip(Skip);
            if (Take > 0) selected = selected.Take(Take);

            var data = selected.ToList();

            var imagePathThumb = string.Format(this._imagePath, companyId);
            foreach (var item in data)
            {
                item.ImagePath = this.VirtualPath(imagePathThumb, item.ImagePath);
            }

            return data;
        }

        public List<DataComplexDto> GetDataByLevel(int companyId, string languageId, List<int?> ids, int level = 1)
        {
            var query = this._categoryLanguageDAL.GetAll()
                .Where(e => e.Category.Item.CompanyId == companyId && e.LanguageId == languageId
                                && e.Category.Item.IsPublished && !e.Category.Item.HasDeleted);

            var queryroot  = ids.Count > 0 ? query.Where(e => ids.Contains(e.CategoryId)) : query;

            var dtos = queryroot.OrderBy(e => e.Category.Item.Orders).Select(e => new DataComplexDto
                {
                    CategoryId = e.CategoryId,
                    CategoryName = e.Title,
                    Type = e.Category.TypeId
                })
                .ToList();

            var datas = new List<DataComplexDto>();
            foreach (var id in ids)
            {
                var dto = dtos.FirstOrDefault(e => e.CategoryId == id);
                if (dto != null) datas.Add(dto);
            }

            if (level > 1)
            {
                // moi support n= 2, nhieu hon phai lam ham de quy
                for (int i = 1; i < level; i++)
                {
                    foreach(var dto in datas)
                    {
                        dto.Childs = query.Where(e => e.Category.ParentId == dto.CategoryId)
                                        .OrderBy(e => e.Category.Item.Orders)
                                        .Select(e => new DataComplexDto
                                        {
                                            CategoryId = e.CategoryId,
                                            CategoryName = e.Title
                                        })
                                        .ToList();
                    }
                }
            }
        
            return datas;
        }

        /// <summary>
        /// Get category by parent id
        /// </summary>
        /// <param name="companyId">ma cong ty </param>
        /// <param name="parentId">
        /// The parent Id.
        /// </param>
        /// <param name="typeId">
        /// The type Id.
        /// </param>
        /// <param name="language">
        /// The language.
        /// </param>
        /// <param name="totalPage">
        /// out count record
        /// </param>
        /// <param name="skip">
        /// From Page
        /// </param>
        /// <param name="take">
        /// Select Top take
        /// </param>
        /// <returns>
        /// List category
        /// </returns>
        public IList<CategoryDto> GetByParentAndTypeId(int companyId, int? parentId, string typeId, string language, out int totalPage, int skip = 0, int take = 0)
        {
            var catQuery = this._categoryLanguageDAL.GetAllIncludeItem()
                .Where(o => o.LanguageId == language && o.Category.Item.CompanyId == companyId)
                .Where(c => c.Category.Item.HasDeleted == false);

            if (!string.IsNullOrEmpty(typeId))
                catQuery = catQuery.Where(o => o.Category.TypeId == typeId);

            var dtoQuery = catQuery.Select(o => new CategoryDto
            {
                ID = o.CategoryId,
                Title = o.Title,
                Description = o.Description,
                ImageName = o.Category.Image,
                Orders = o.Category.Item.Orders,

                ParentId = o.Category.ParentId,
                TypeId = o.Category.TypeId
            });

            if (parentId != null)
            {
                var categoryIds = this._categoryDAL.GetAllChildId(parentId, companyId);

                if (categoryIds != null) dtoQuery = dtoQuery.Where(o => categoryIds.Contains(o.ID));
            }

            totalPage = dtoQuery.Count();
            dtoQuery = dtoQuery.OrderBy(o => o.Orders).ThenByDescending(o => o.ID);
            if (skip > 0) dtoQuery = dtoQuery.Skip(skip);
            if (take > 0) dtoQuery = dtoQuery.Take(take);

            var result = dtoQuery.ToList();

            // update path image
            var imagePath = string.Format(this._imagePath, companyId);
            result.ForEach(dto => dto.ImagePath = this.VirtualPath(imagePath, dto.ImageName));

            return result;
        }

        /// <summary>
        /// Get category by parent id
        /// </summary>
        /// <param name="companyId">Mã công ty</param>
        /// <param name="parentId">
        /// The parent Id.
        /// </param>
        /// <param name="typeId">
        /// The type Id.
        /// </param>
        /// <param name="language">
        /// The language.
        /// </param>
        /// <param name="hasChild"> </param>
        /// <returns>
        /// List category
        /// </returns>
        public IList<CategorySimpleDto> GetByTypeId(int companyId, int parentId, string typeId, string language, bool hasChild)
        {
            var catQuery = this._categoryLanguageDAL.GetAllIncludeItem()
                .Where(e => e.LanguageId == language && e.Category.Item.IsPublished && e.Category.Item.CompanyId == companyId && !e.Category.Item.HasDeleted);

            if (!string.IsNullOrEmpty(typeId))
                catQuery = catQuery.Where(o => o.Category.TypeId == typeId);

            var dtoQuery = catQuery.OrderBy(e => e.Category.Item.Orders)
                .Select(o => new CategorySimpleDto
            {
                ID = o.CategoryId,
                Title = o.Title,
                ParentId = o.Category.ParentId,
            });

            if (hasChild)
            {
                var categoryIds = this._categoryDAL.GetAllChildId(parentId, companyId);

                if (categoryIds != null) dtoQuery = dtoQuery.Where(o => categoryIds.Contains(o.ID));
            }
            else
            {
                dtoQuery = (parentId == 0)
                    ? dtoQuery.Where(o => o.ParentId == null)
                    : dtoQuery.Where(o => o.ParentId == parentId);
            }

            var data = dtoQuery.ToList();

            return data;
        }

        /// <summary>
        /// Danh mục và đếm sản phẩm từng danh mục
        /// </summary>
        /// <param name="companyId">Mã công ty</param>
        /// <param name="parentId">
        /// The parent Id.
        /// </param>
        /// <param name="language">
        /// The language.
        /// </param>
        /// <param name="hasChild"> </param>
        /// <returns>
        /// List category
        /// </returns>
        public IList<CategoryCountDto> GetByTypeCountProduct(int companyId, int? parentId, string language, bool hasChild)
        {
            var catQuery = this._categoryLanguageDAL.GetAllIncludeItem()
                .Where(e => e.Category.Item.IsPublished && e.Category.Item.CompanyId == companyId)
                .Where(e => e.LanguageId == language && e.Category.TypeId == "PRO");

            var dtoQuery = catQuery.Select(o => new CategoryCountDto
            {
                ID = o.CategoryId,
                Title = o.Title,
                ParentId = o.Category.ParentId,
                Count = o.Category.Products.Where(e => e.Item.IsPublished).Count(p => p.Item.CompanyId == companyId)
            });

            if (hasChild)
            {
                var categoryIds = this._categoryDAL.GetAllChildId(parentId, companyId);

                if (categoryIds != null) dtoQuery = dtoQuery.Where(o => categoryIds.Contains(o.ID));
            }
            else
            {
                dtoQuery = dtoQuery.Where(o => o.ParentId == parentId);
            }

            return dtoQuery.ToList();
        }

        /// <summary>
        /// Create Option tag by List Category
        /// </summary>
        /// <param name="category">Data Source</param>
        /// <param name="parentId">Id Root</param>
        /// <param name="space">character prefix</param>
        /// <param name="group">Can Select Parent</param>
        /// <param name="selectParent">select defaut parent of default</param>
        /// <param name="selected">select default</param>
        /// <returns>string Tag</returns>
        public string GenTagOption(IList<CategorySimpleDto> category, int? parentId, string space = "", bool group = false, bool selectParent = false, int selected = 0)
        {
            var subcats = category.Where(o => o.ParentId == parentId).ToList();
            if (subcats.Count == 0)
            {
                return null;
            }

            var listMenu = string.Empty;

            foreach (var subcat in subcats)
            {
                var spaceNext = space + "&nbsp;&nbsp;&nbsp;";
                var res = this.GenTagOption(category, subcat.ID, spaceNext, @group, selectParent, selected);

                string select;
                if (selectParent)
                {
                    select = category.Any(o => o.ID == selected && o.ParentId == subcat.ID) ? "selected=selected" : string.Empty;
                }
                else
                {
                    select = selected == subcat.ID ? "selected=selected" : string.Empty;
                }

                if (group)
                {
                    if (res != null)
                    {
                        listMenu += "<optgroup label='" + space + subcat.Title + "'>";
                        listMenu += res;
                        listMenu += "</optgroup>";
                    }
                    else
                    {
                        listMenu += "<option value=" + subcat.ID + " " + select + " >" + space + subcat.Title + "</option>";
                    }
                }
                else
                {
                    listMenu += "<option value=" + subcat.ID + " " + select + ">" + space + subcat.Title + "</option>";
                    if (res != null)
                    {
                        listMenu += res;
                    }
                }
            }

            return listMenu;
        }

        /// <summary>
        /// Get category by id ang language
        /// </summary>
        /// <param name="id">
        /// The Category Id.
        /// </param>
        /// <param name="language">
        /// The language.
        /// </param>
        /// <param name="companyId">
        /// The company Id.
        /// </param>
        /// <returns>
        /// The category
        /// </returns>
        public CategoryDto GetByIdAndLanguage(int id, string language, int companyId)
        {
            var dtoQuery = this._categoryLanguageDAL.GetAllIncludeItem()
                .Where(c => c.LanguageId == language && c.CategoryId == id)
                .Where(c => !c.Category.Item.HasDeleted && c.Category.Item.IsPublished)
                .Select(o => new CategoryDto
                {
                    ID = o.CategoryId,
                    Title = o.Title,
                    Description = o.Description,
                    ImageName = o.Category.Image,
                    Orders = o.Category.Item.Orders,
                    ParentId = o.Category.ParentId,
                    TypeId = o.Category.TypeId
                });

            var dto = dtoQuery.FirstOrDefault();

            // update path image
            if (dto != null)
            {
                var imagePath = string.Format(this._imagePath, companyId);
                dto.ImagePath = this.VirtualPath(imagePath, dto.ImageName);
            }

            return dto;
        }

        public IList<CategorySimpleDto> GetListCategory(string languageId, int parentId = 0, string ortherId = "")
        {
            var cats = this._categoryLanguageDAL.GetMany(c => c.LanguageId == languageId).Include(c => c.Category).Include(c => c.Category.Item);
            if (parentId > 0) cats = cats.Where(c => c.Category.ParentId == parentId);
            if (ortherId.Length > 0)
            {
                var lst = ortherId.Split(',').ToList();
                foreach (var i in lst)
                {
                    var id = Convert.ToInt32(i);
                    cats = cats.Where(c => c.CategoryId != id);
                }
            }
            var result = cats.OrderBy(c => c.Category.Item.Orders).Select(c => new CategorySimpleDto { ID = c.CategoryId, Title = c.Title });
            return result.ToList();
        }

        public IList<CategoryLanguage> GetAllCategoryLanguage()
        {
            var cats = this._categoryLanguageDAL.GetAll();
            return cats.ToList();
        }
        public IList<CategoryLanguage> GetCategoryLanguageByLanguage(string languageId)
        {
            var cats = this._categoryLanguageDAL.GetMany(o => o.LanguageId == languageId);
            return cats.ToList();
        }

        public IList<CategoryLanguage> GetCategoryIncludeProductByParent(int parentId, string languageId)
        {
            var cats = this._categoryLanguageDAL.GetMany(c => c.LanguageId == languageId).Include(c => c.Category).Include(c => c.Category.Item);
            if (parentId > 0) cats = cats.Where(c => c.Category.ParentId == parentId);
            var result = cats.Include(c => c.Category.Products);
            return result.OrderBy(c => c.Category.Item.Orders).ToList();
        }
        public IList<CategoryLanguage> GetCategoryLanguageByParent(int parentId, string language)
        {
            var catsLang = this._categoryLanguageDAL.GetAll()
                .Include(c => c.Category).Include(c => c.Category.Item)
                .Where(c => c.Category.ParentId == parentId && c.Category.Item.HasDeleted == false && c.Category.Item.IsPublished)
                .Select(c => new CategoryLanguage
                            {
                                CategoryId = c.CategoryId,
                                Description = c.Description,
                                LanguageId = c.LanguageId,
                                Title = c.Title
                            });
            return catsLang.ToList();
        }

        /// <summary>
        /// Get list parent id
        /// </summary>
        /// <param name="languageId">Mã ngôn ngữ</param>
        /// <param name="categoryType">Loại danh mục</param>
        /// <param name="categoryId">Category id</param>
        /// <param name="companyId">Mã công ty</param>
        /// <returns>List Id</returns>
        public IList<CategorySimpleDto> GetAllParentId(int companyId, string languageId, string categoryType, int? categoryId)
        {
            var query = this._categoryLanguageDAL.GetAll()
                .Where(e => e.Category.Item.IsPublished && e.Category.Item.HasDeleted == false)
                .Where(e => e.Category.Item.CompanyId == companyId && e.LanguageId == languageId)
                .Where(e => e.Category.TypeId.ToUpper() == categoryType.ToLower())
                .Select(e => new CategorySimpleDto
                    {
                        ID = e.Category.Id,
                        ParentId = e.Category.ParentId,
                        Title = e.Title
                    });

            var data = query.ToList();

            var result = this.GetAllParent(data, categoryId);

            return result;
        }
        #endregion

        #region Private method

        /// <summary>
        /// Get list parent id
        /// </summary>
        /// <param name="category">Danh sách category</param>
        /// <param name="categoryId">Category id</param>
        /// <returns>List Id</returns>
        private IList<CategorySimpleDto> GetAllParent(IList<CategorySimpleDto> category, int? categoryId)
        {
            var parent = category.FirstOrDefault(o => o.ID == categoryId);
            if (parent == null) return null;
            var result = new List<CategorySimpleDto>();

            while (parent != null)
            {
                result.Add(parent);
                parent = category.FirstOrDefault(o => o.ID == parent.ParentId);
            }

            return result;
        }

        private string VirtualPath(string folderPath, string fileName)
        {
            return string.IsNullOrEmpty(fileName) ? string.Empty : string.Format("{0}{1}{2}", this._domainStore, folderPath, fileName);
        }
        #endregion
    }
}